/**
 * This software was produced for the U. S. Government
 * under Contract No. FA8721-09-C-0002, and is
 * subject to the Rights in Noncommercial Computer Software
 * and Noncommercial Computer Software Documentation Clause
 * (DFARS) 252.227-7014 (JUN 1995)
 * 
 * (c) 2009 The MITRE Corporation. All Rights Reserved.
 *
 * SPMIF to OPNET Translator
 * Author: ewalters
 * Created: 11:33:29 AM, Aug 3, 2007
 * MITRE Corporation
 */
package com.mitre.spmif2opnet.analysis;

import java.util.Collection;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.Properties;
import java.util.Queue;
import java.util.Set;

import com.mitre.spmif2opnet.analysis.ast.Node;
import com.mitre.spmif2opnet.analysis.ast.Project;
import com.mitre.spmif2opnet.analysis.ast.Scenario;

/**
 * This class is a holder for the analysis (e.g., vars and graph manipulation)
 * results for the SPMIF-IR translation.
 */
public class AnalysisContext {

	/**
	 *  The identifier for the node we're currently constructing
	 */
	private Identifier         _curr_id;
	/**
	 *  Map from project name to project
	 */
	private Hashtable<String, Project> _project_map;
	/**
	 *  Work list for variable resolution pass
	 */
	private Queue<Symbol>      _var_work_list;
	/**
	 *  Have we done the resolution pass?
	 */
	private boolean            _vars_resolved;
	/**
	 *  What are the partitions of scenarios induced by sync node?
	 *  Each partition = one task
	 */  
	private ScenarioPartitions _partitions;
	/**
	 *  How do we calculate priorities
	 */
	private Priorities         _priorities;
	/**
	 *  Track which files we have already loaded in (e.g., to get external scenarios)
	 */
	private Set<String>        _files_loaded;
	/**
	 *  Previous node to use if we are jumping around (e.g., sync nodes)
	 */
	private Node               _prev_processing_node;
	/**
	 *  External Properties
	 */
	private Properties         _props;
	
	/**
	 * Analysis constructor
	 * @param ptype what kinds of priorities to track
	 * @param props what are the Java external properties
	 */
	public AnalysisContext(Priorities.PriorityType ptype, Properties props) {
		_curr_id = new Identifier();
		_project_map = new Hashtable<String, Project>();
		_var_work_list = new LinkedList<Symbol>();
		_vars_resolved = false;
		_partitions = new ScenarioPartitions();
		_priorities = ptype.makePriorities();
		_files_loaded = new HashSet<String>();
		_prev_processing_node = null;
		_props = props;
	}
	
	/**
	 *  File loading accessor/mutator
	 *  Remember S-PMIF descriptions can be in multiple files!
	 * @param file_name add a file to be loaded
	 */
	public void setFileLoaded(String file_name) {
		_files_loaded.add(file_name);
	}
	
	/**
	 * Is the state file already loaded?
	 * @param file_name the file to check
	 * @return was the file loaded?
	 */
	public boolean isFileLoaded(String file_name) {
		return _files_loaded.contains(file_name);
	}

	// *** Id manipulation routines
	
	/**
	 * Current ID file name mutator
	 * @param s the file name
	 */
	public void setFileName(String s) {
		_curr_id.setFileName(s);
	}
	
	/**
	 * Current id project name mutator
	 * @param s the project name
	 */
	public void setProjectName(String s) {
		_curr_id.setProjectName(s);
	}
	
	/**
	 * Current id scenario name mutator
	 * @param s the scenario name
	 */
	public void setScenarioName(String s) {
		_curr_id.setScenarioName(s);
	}
	
	/**
	 * Current graph id mutator
	 * @param s the graph id
	 */
	public void setGraphId(String s) {
		_curr_id.setGraphId(s);
	}
	
	/**
	 * What is the current node identifier?
	 * @return node id
	 */
	public Identifier getCurrId() {
		return _curr_id;
	}
	
	/**
	 * What are the scenario partitions?
	 * @return partitioning
	 */
	public ScenarioPartitions getPartitions() {
		return _partitions;
	}
	
	/**
	 * Add a project name to the project map
	 * @param p the project to add
	 */
	public void addProject(Project p) {
		_project_map.put(p.getName(), p);
	}
	
	/**
	 * Has the file containing the desired scenario been loaded?
	 * @param file_name the file
	 * @param project_name the project containing the scenario
	 * @return was it loaded?
	 */
	public boolean scenarioLoaded(String file_name, String project_name) {
		if (_project_map.containsKey(project_name)) {
			Project p = _project_map.get(project_name);
			String s = p.scenFromFile(file_name);
			return s != null;
		} 
		return false;
	}

	/**
	 * Accessor from project name to project
	 * @param proj_name the project name
	 * @return the project
	 */
	public Project getProject(String proj_name) {
		return _project_map.get(proj_name);
	}
	
	/**
	 * Add a symbol to the variable work list - used for constructing the IR graph
	 * @param sym the symbol to add
	 */
	public void addSymbol(Symbol sym) {
		_var_work_list.add(sym);
	}
	
	/**
	 * Resolve the variable reference given to us by S-PMIF using a work list.
	 */
	public void resolveVars() {
		if (_vars_resolved) return;
		// Work through the list, resolving symbols
		// NOTE: This list WILL change as link/sync nodes are pulled in
		while (!_var_work_list.isEmpty()) {
			_var_work_list.remove().resolve(this);
		}
		_vars_resolved = true;
	}

	/** 
	 * Return all of the projects in the project map 
	 * Good for summaries
	 * @return the projects
	 */
	public Collection<Project> getProjects() {
		return _project_map.values();
	}
	
	// *** Priority determination

	/**
	 * Priority fetching via (project, scenario)
	 * @param proj_name what is the project
	 * @param scen_name what scenario within the project
	 * @return the number priority
	 */
	public int getPriority(String proj_name, String scen_name) {
		return _priorities.getOpnetPriority(proj_name, scen_name);
	}
	
	/**
	 * Add a priority to the map - the numbers are calculated in 
	 *  "calculatePriorities"
	 * @param proj_name which project
	 * @param scen which scenario
	 */
	public void addPriority(String proj_name, Scenario scen) {
		_priorities.addSpmifPriority(proj_name, scen);
	}
	
	/**
	 * Calculate the priorities 
	 * @throws AnalysisException
	 */
	public void calculatePriorities() throws AnalysisException {
		_priorities.calculate();
	}
	
	/** 
	 * Return the entire priorities structure
	 * @return
	 */
	public Priorities getPriorities() {
		return _priorities;
	}
	
	/**
	 * Set the priorities structure
	 * @param p
	 */
	public void setPriorities(Priorities p) {
		_priorities = p;
	}

	// *** Previous node access
	
	/**
	 *  Do we have a previous node?
	 * @return
	 */
	public boolean hasPrevProcNode() {
		return !(_prev_processing_node == null);
	}
	
	/**
	 * Set the previous node
	 * @param n the node
	 */
	public void setPrevProcNode(Node n) {
		_prev_processing_node = n;
	}
	
	/**
	 * Get the previous Node
	 * @return
	 */
	public Node getPrevProcNode() {
		return _prev_processing_node;
	}
	
	/**
	 * What properties are we examining?
	 * @return the properties
	 */
	public Properties getProps() {
		return _props;
	}
}

